﻿using System;
using System.IO;
using System.Reflection;
using Pfz.Remoting.Instructions;
using Pfz.Serialization;

namespace Pfz.Remoting.Internal
{
	internal sealed class _ThreadData:
		IDisposable
	{
		private readonly RemotingSerializer Serializer;
		private IChannel _channel;
		private readonly Stream Stream;
		private readonly object _wrappersLock;

		public _ThreadData(IChannel channel, RemotingClient client)
		{
			_wrappersLock = client._wrappersLock;
			_channel = channel;
			Stream = channel.Stream;
			Serializer = new RemotingSerializer(client);
				
			Serializer.AddRecommendedDefaults();

			Serializer.AddDefaultType(typeof(long[]));
			Serializer.AddDefaultType(typeof(Type[]));

			Serializer.AddDefaultType(typeof(InstructionCreateObject));
			Serializer.AddDefaultType(typeof(InstructionAddEvent));
			Serializer.AddDefaultType(typeof(InstructionRemoveEvent));
			Serializer.AddDefaultType(typeof(InstructionGetProperty));
			Serializer.AddDefaultType(typeof(InstructionInvokeMethod));
			Serializer.AddDefaultType(typeof(InstructionInvokeStaticMethod));
			Serializer.AddDefaultType(typeof(InstructionSetProperty));
			Serializer.AddDefaultType(typeof(InstructionInvokeDelegate));
			Serializer.AddDefaultType(typeof(InstructionCollect));

			Serializer.AddDefaultType(typeof(_Reference));
			Serializer.AddDefaultType(typeof(_Wrapped));
			Serializer.AddDefaultType(typeof(_WrappedDelegate));
			Serializer.AddDefaultType(typeof(RemotingResult));
			Serializer.AddDefaultType(typeof(_BackObjectReference));
			Serializer.AddDefaultType(typeof(_BackDelegateReference));
		}
		public void Dispose()
		{
			Disposer.Dispose(ref _channel);
		}

		public void Serialize(object data)
		{
			Serializer.Serialize(Stream, data);
			Stream.Flush();
		}
		public object Deserialize()
		{
			return Serializer.Deserialize(Stream, _wrappersLock);
		}
		internal object DeserializeNoLock()
		{
			return Serializer.Deserialize(Stream, null);
		}

		public int LastDeserializeWrapCount
		{
			get
			{
				return Serializer._wrapCount;
			}
		}

		internal void _Action(Func<object> action)
		{
			var result = new RemotingResult();
			try
			{
				result.Value = action();
			}
			catch(Exception exception)
			{
				if (exception.GetBaseException() is RemotingSyncException)
					throw;

				result.Exception = exception;
			}

			Serialize(result);
		}
		internal void _Action(MethodInfo methodInfo, object[] outParameters, Func<object> action)
		{
			var result = new RemotingResult();
			try
			{
				result.Value = action();
				result.OutValues = RemotingClient._GetOutValues(methodInfo, outParameters);
			}
			catch(Exception exception)
			{
				if (exception.GetBaseException() is RemotingSyncException)
					throw;

				result.Exception = exception;
			}

			Serialize(result);
		}
	}
}
